using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using interfaces;

namespace retrofit
{
    public class Input
    {
        /// the input index for selected input variable
        public int[] m_inputIndex;

		/// <summary>
		/// continuous variable bound type
		/// </summary>
		public enum InputBound_T
		{
			Lower_IBT,		// lower bound
			Upper_IBT,		// upper bound
			NUM_IBT
		};

//		public static string[][] m_input_str;

		/// <summary>
		/// group index, with low bound and high bound index.
		/// this is created for continuous variable interpolation.
		/// 1D access via Input.InputBound_T, 2D access via Keyword.keyword_t; 
		/// </summary>
		public int[][] m_groupIndex;

		/// <summary>
		/// the original string input.
		/// </summary>
		public string[] m_strInput;

		/// **** V2 definition below ** ///

		/// <summary>
		/// measure list containing relavent measure for selected building type.
		/// </summary>
		public List<DmMeasure> m_measureList;

		/// <summary>
		/// measure list dictionary list for mapping measure code to measure index.
		/// </summary>
		public List<Dictionary<string, int>> m_dictionaryList;


		/// <summary>
		/// constructor.
		/// </summary>
		public Input()
		{
			m_inputIndex = new int[(int)Keyword.keyword_t.NUM_KWT];
			m_groupIndex = new int[(int)InputBound_T.NUM_IBT][] { new int[(int)Keyword.keyword_t.NUM_KWT], new int[(int)Keyword.keyword_t.NUM_KWT] };
			m_strInput = new string[(int)Keyword.keyword_t.NUM_KWT];

			List<DmMeasure> measure_list = new List<DmMeasure>();

			List<Dictionary<string, int>> dictionary_list = new List<Dictionary<string, int>>();
			dictionary_list.Add(new Dictionary<string, int>());
			dictionary_list.Add(new Dictionary<string, int>());

//			for (int i = 0; i < (int)Keyword.keyword_t.NUM_KWT; ++i)
//			{
//				m_input_str[i] = new string[Keyword.m_inputs_num[i]];
//			}
		}

		/// <summary>
		/// this method will compare the input variable string (input_str) with the keyword.m_inputs string
		/// once a matching string is found for discreet variable, the mathing string's index is stored in m_inputIdx.
		/// for continuous variables, we use the closed distance method to find the matching string and its index.
		/// </summary>
		/// <param name="input_str">the input string array from GUI</param>
		/// <returns>true, for successful finding input string index</returns>
		public void createInputIdx(string[] input_strs, List<string> err_msgs)
        {
			if (input_strs.Length != (int)Keyword.keyword_t.NUM_KWT)
			{
				err_msgs.Add("The input variable dimension does not match with ECMs dimension");
				return;
			}

            for (int i = 0; i < (int)Keyword.keyword_t.NUM_KWT; ++i)
            {
				string input_str = input_strs[i];

				m_inputIndex[i] = createAnIdx(input_str, (Keyword.keyword_t)i, err_msgs);

				int[] lower_upper = createLowerUpperIdx(input_str, (Keyword.keyword_t)i, err_msgs);
				m_groupIndex[(int)InputBound_T.Lower_IBT][i] = lower_upper[(int)InputBound_T.Lower_IBT];
				m_groupIndex[(int)InputBound_T.Upper_IBT][i] = lower_upper[(int)InputBound_T.Upper_IBT];
            }

			m_strInput = input_strs;

            return;
        }
		
		/// <summary>
		/// create one ECM input index.
		/// </summary>
		/// <param name="input_str">the input string variable from GUI</param>
		/// <param name="kw">current ECM keyword, access via Keyword.keyword_t</param>
		/// <param name="err_msgs">error msgs list</param>
		/// <returns>the found keyword index</returns>
		public static int createAnIdx(string input_str, Keyword.keyword_t kw, List<string> err_msgs)
		{
			int kw_idx = (int)kw;
			int index = -1;

			for (int j = 0; j < (int)Keyword.m_inputs_selected_num[kw_idx]; ++j)
			{
				int idx = Keyword.m_inputs_selected[kw_idx][j];  // the keyword index based on selected keyword, starting from 0
				//Console.WriteLine(i + ", "+ idx);
				if (input_str == Keyword.m_inputs[kw_idx][idx])
				{
					index = idx;
					//Console.WriteLine(i + ", " + (idx+1) + ", "+ Keyword.m_inputs[(int)i][idx]);
					break;
				}
			}

			// we don't have a match of string found.
			if (index < 0)
			{
				//Console.WriteLine("Found index: " + index);

				if (interf.BEE_enumMeasureInputType[kw_idx] == interf.BEE_Input_T.Discrete_IT)
				{
					//err_msgs.Add("Unknown input: '" + input_str + "' is found for ECM keyword: " + Keyword.m_keyword[kw_idx] + ". The program is terminated");
					return index;
				}

				index = distance(input_str, (Keyword.keyword_t)kw_idx);
				//Console.WriteLine("Found index: " + index);

				// if we still have index unfound, we return false
				if (index <= 0)
				{
					//err_msgs.Add("Unknown input: '" + input_str + "' is found for ECM keyword: " + Keyword.m_keyword[kw_idx] + ". The program is terminated");
					return index;
				}
			}

			return index;
		}

		/// <summary>
		/// create lower upper index for continuous variable
		/// </summary>
		/// <param name="input_str">ECM input string</param>
		/// <param name="kw">ECM keyword</param>
		/// <param name="err_msgs">err message list</param>
		/// <returns>lower and upper index output array</returns>
		public static int[] createLowerUpperIdx(string input_str, Keyword.keyword_t kw, List<string> err_msgs)
		{
			int kw_idx = (int)kw;
			int index = -1;

			int[] lower_upper = new int[(int)InputBound_T.NUM_IBT];

			for (int j = 0; j < (int)Keyword.m_inputs_selected_num[kw_idx]; ++j)
			{
				int idx = Keyword.m_inputs_selected[kw_idx][j];  // the keyword index based on selected keyword, starting from 0
				//Console.WriteLine(i + ", "+ idx);
				if (Convert.ToDouble(input_str) == Convert.ToDouble(Keyword.m_inputs[kw_idx][idx]))		// for discrete variable and continuous variable which is inputed at our pre-set input point.
				{
					index = idx;                  // our input index start from 1 not 0
					//Console.WriteLine(i + ", " + (idx+1) + ", "+ Keyword.m_inputs[(int)i][idx]);
					lower_upper[(int)InputBound_T.Lower_IBT] = index;
					lower_upper[(int)InputBound_T.Upper_IBT] = index;
					break;
				}
			}

			// we don't have a match of string found.
			if (index < 0)
			{
				//Console.WriteLine("Found index: " + index);

				if (interf.BEE_enumMeasureInputType[kw_idx] == interf.BEE_Input_T.Discrete_IT)
				{
					err_msgs.Add("Unknown input: '" + input_str + "' is found for ECM keyword: " + Keyword.m_keyword[kw_idx] + ". The program is terminated");
					return lower_upper;
				}

				lower_upper = lowerUpper(input_str, (Keyword.keyword_t)kw_idx);
				//Console.WriteLine("Found index: " + index);

				// if we still have index unfound, we return false
				if (lower_upper[(int)InputBound_T.Lower_IBT] < 0 || lower_upper[(int)InputBound_T.Upper_IBT] < 0)
				{
					err_msgs.Add("Unknown input: '" + input_str + "' is found for ECM keyword: " + Keyword.m_keyword[kw_idx] + ". The program is terminated");
					return lower_upper;
				}
			}

			return lower_upper;
		}


        /// <summary>
        /// this function will find the closest point of input continuous variable
        /// </summary>
        /// <param name="input">the input continuous variable string</param>
		/// <param name="kw">the input continuous variable keyword</param>
        /// <returns>the index which is closed to string input</returns>
        private static int distance(string input, Keyword.keyword_t kw)
        {
            // the keyword integer number
            int int_kw = (int)kw;

            // we start looping from the 2nd element in the list.
            for (int i = 1; i < Keyword.m_inputs_selected_num[int_kw]; ++i)
            {
                // we find actual keyword index based on selected predefined index.
                int previous_idx = Keyword.m_inputs_selected[int_kw][i - 1] ;  // index is -1 from selected instance index.
                int next_idx = Keyword.m_inputs_selected[int_kw][i];

                double input_d = Convert.ToDouble(input);
                double previous = Convert.ToDouble(Keyword.m_inputs[(int)kw][previous_idx]);
                double next = Convert.ToDouble(Keyword.m_inputs[(int)kw][next_idx]);

                // for the first item in the keyword list, if input value is still smaller than this,
                // then we use the first item and store its index
                if (i == 1 & input_d <= previous)
                {
                    int idx = Keyword.m_inputs_selected[(int)kw][i - 1];
                    // Console.WriteLine((int)kw + ", " + idx + ", " + Keyword.m_inputs[(int)kw][idx]);
                    return idx;
                }

                // for the last item in the keyword list, if input value is bigger than this,
                // then we use the last item and store its index.
                if (i == Keyword.m_inputs_selected_num[int_kw] - 1 & input_d >= next)
                {
                    //Console.WriteLine((int)kw + ", " + Keyword.m_inputs[(int)kw][i]);
                    return Keyword.m_inputs_selected[int_kw][i];
                }

                // we check if input value is in between previous and next value range
                if (input_d >= previous & input_d < next)
                {
                    // closer to next value
                    if (input_d - previous >= next - input_d)
                        return Keyword.m_inputs_selected[int_kw][i];
                    // closer to previous value
                    else if (input_d - previous < next - input_d)
                        return Keyword.m_inputs_selected[int_kw][i-1];
                }
            }
            return 0;
        }

		/// <summary>
		/// create lower upper index for continuous variable
		/// </summary>
		/// <param name="input">string input</param>
		/// <param name="kw">ECM continuous variable keywrod</param>
		/// <returns>the upper and lower index array, access via Input.InputBound_T</returns>
		private static int[] lowerUpper(string input, Keyword.keyword_t kw)
		{
			// the keyword integer number
			int int_kw = (int)kw;
			int[] lower_upper = new int[(int)InputBound_T.NUM_IBT];

			for (int i = 1; i < Keyword.m_inputs_selected_num[int_kw]; ++i)
			{
				// we find actual keyword index based on selected predefined index.
				int previous_idx = Keyword.m_inputs_selected[int_kw][i - 1];  // index is -1 from selected instance index.
				int next_idx = Keyword.m_inputs_selected[int_kw][i];

				double input_d = Convert.ToDouble(input);
				double previous = Convert.ToDouble(Keyword.m_inputs[(int)kw][previous_idx]);
				double next = Convert.ToDouble(Keyword.m_inputs[(int)kw][next_idx]);

				// for the first item in the keyword list, if input value is still smaller than this,
				// then we use the first item and store its index
				if (i == 1 & input_d <= previous)
				{
					int idx = Keyword.m_inputs_selected[(int)kw][i - 1];
					lower_upper[(int)InputBound_T.Lower_IBT] = idx;
					lower_upper[(int)InputBound_T.Upper_IBT] = idx;
					// Console.WriteLine((int)kw + ", " + idx + ", " + Keyword.m_inputs[(int)kw][idx]);
					return lower_upper;
				}

				// for the last item in the keyword list, if input value is bigger than this,
				// then we use the last item and store its index.
				if (i == Keyword.m_inputs_selected_num[int_kw] - 1 & input_d >= next)
				{
					//Console.WriteLine((int)kw + ", " + Keyword.m_inputs[(int)kw][i]);
					int idx = Keyword.m_inputs_selected[int_kw][i];
					lower_upper[(int)InputBound_T.Lower_IBT] = idx;
					lower_upper[(int)InputBound_T.Upper_IBT] = idx;

					return lower_upper;
				}

				// we check if input value is in between previous and next value range
				if (input_d >= previous & input_d < next)
				{
					// closer to previous value
					lower_upper[(int)InputBound_T.Lower_IBT] = Keyword.m_inputs_selected[int_kw][i - 1];
					// closer to next value
					lower_upper[(int)InputBound_T.Upper_IBT] = Keyword.m_inputs_selected[int_kw][i];

					return lower_upper;
				}
			}
			return lower_upper;
		}
	
	}
}
